/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.graph.algo.ksp;

import combi.graph.Mask;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import combi.wip.graph.algo.ksp.LabelGroupeFromMap;
import combi.wip.graph.algo.ksp.LabelGroupeSommets;

import org.jgrapht.graph.DefaultDirectedWeightedGraph;
//import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DefaultWeightedEdge;


public class Ksp_Yen_P2P_mt {

	private static int NB_THREADS = 3;
	//private static String pathPrefix = "./ecocyc - ALL-CHORISMATE-PWY - 53/";
	//private static String pathPrefix = "./ecocyc/";
	//private static String pathPrefix = System.getProperty("user.home") + "/data/eco_shuffle3_meta/";
	//private static String pathPrefix = "./data/";
	//private static String pathPrefix = "./eco_hack_menD_menH/";
	//private static String pathPrefix = System.getProperty("user.home") + "/Data/Chili/meta_reaction/";
	private static String pathPrefix = System.getProperty("user.home") + "/Data/Chili/Metacyc/ecoli/Gint/";
	//private static String pathPrefix = "./eco00030/";
	//private static String pathPrefix = "./metabolisme_central/";
	//private static String pathPrefix = "./eco00910_shuffle/";
	//private static String pathPrefix = "./eco00010_fenetre/";
	//private static String pathPrefix = "./eco00010_shuffle_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_fenetre/";
	//private static String pathPrefix = "./eco00010_shuffle_meta/";
	//private static String pathPrefix = "./eco00010_shuffle_meta_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_meta_fenetre/";
	//private static String pathPrefix = "./metabolisme_central_shuffle_meta/";
	//private static String pathPrefix = "./test/";
	//private static String pathPrefix = "./test_shuffle_genome/";
	//private static String pathPrefix = "./test_shuffle_metabolisme/";
	private static String fullPathPrefix = pathPrefix;
	//private static String fullPathPrefix = "./eco/";
	//private static String fullPathPrefix = "./eco_shuffle/";
	//private static String fullPathPrefix = "./eco_shuffle_meta/";

	private static String pathOutput = "./" + java.lang.System.currentTimeMillis() + "/";
	private static String modeleFile = "modele.txt";
	//private static String modeleFile = "graph0.txt";
	//private static String modeleFile = "eco00010.txt";
	private static String fileIn = pathPrefix+modeleFile;

	private static Integer k = 10;

	private static String fenetreFile = pathPrefix + "associationsR.txt";
	//private static String fenetreFile = pathPrefix + "associationsRhack.txt";
	//private static String fenetreFile = pathPrefix + "associationsG.txt";

	private static String associationsFile = fullPathPrefix + "associations.txt";
	private static String associationsRFile = fullPathPrefix + "associationsR.txt";
	private static String associationsGFile = fullPathPrefix + "associationsG.txt";
	private static String associationsMFile = fullPathPrefix + "associationsM.txt";
	//static String associationsFile = fullPathPrefix + "associationsContract.txt";
	//static String associationsRFile = fullPathPrefix + "associationsContractR.txt";
	//static String associationsGFile = fullPathPrefix + "associationsContractG.txt";
	//static String associationsMFile = fullPathPrefix + "associationsContractM.txt"; "/";
	private static String outFile = pathPrefix+pathOutput+"result-"+k+".txt";

	private static String stats = pathPrefix+pathOutput + "stats_" + k +"ksp.txt";
	private DefaultDirectedWeightedGraph<Integer, DefaultWeightedEdge> graph;

	private Collection<Set<Integer>> debElem;
	private Collection<Set<Integer>> finElem;
	private LabelGroupeSommets<Set<Integer>> labels=null;

	// Il doit y avoir une bijection entre les deux partie de la map.
	private static Map<Set<Integer>, String> inverseMap(Map<String, Set<Integer>> map)
	{
		Map<Set<Integer>, String> newMap = new HashMap<Set<Integer>, String>();
		for (Entry<String, Set<Integer>> entry : map.entrySet())
		{
			String old = newMap.put(entry.getValue(), entry.getKey());
			if(old != null)
			{
				System.err.println(old);
			}
		}
		return newMap;
	}
	
	public static void main(String[] args) {
		//String fileIn = fullPathPrefix+modeleFile;
		pathPrefix = "./data/";
		pathOutput = "./";
		//pathPrefix = System.getProperty("user.home") + "/Data/Chili/meta_reaction/";
		//pathOutput = "./" + java.lang.System.currentTimeMillis() + "/";
		fileIn = pathPrefix+"test_50";
		outFile = pathPrefix+pathOutput+"result-"+k+".txt";
		File fb = new File(pathPrefix+pathOutput); 
		fb.mkdirs();
		try{
			Set<String> fenetre = loadAssoReaction(fenetreFile).keySet();
			//Set<String> fenetre = loadAssoGene(fenetreFile).keySet();
			System.err.println(fenetre);

			Map<String, Set<Integer>> reactionsAsso = loadAssoReaction(associationsRFile);
			//System.out.println(reactionsAsso);
			Map<String, Set<Integer>> genesAsso =loadAssoGene(associationsGFile);
			//System.out.println(genesAsso);

			Map<String, Set<Integer>> metabolitesAsso =loadAssoGene(associationsMFile);
			//System.out.println(metabolitesAsso);
			reactionsAsso.putAll(metabolitesAsso);

			Map<String, Set<Integer>> fenetreBase=reactionsAsso;

			Set<String> debElem = fenetre;
			List<Set<Integer>> debSet = new ArrayList<Set<Integer>>();
			for (String debut : debElem)
			{
				if (fenetreBase.containsKey(debut))
				{
					Set<Integer> entry1 = fenetreBase.get(debut);
					debSet.add(entry1);
				}
			}

			Set<String> finElem = fenetre;
			List<Set<Integer>> endSet = debSet;
			
			Ksp_Yen_P2P_mt algo = new Ksp_Yen_P2P_mt(fileIn, debSet, endSet, k, outFile);
			algo.setLabelsGroupes(new LabelGroupeFromMap<Set<Integer>>(inverseMap(reactionsAsso)));
			algo.run();
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
	}
	

	public Ksp_Yen_P2P_mt(String graph, Collection<Set<Integer>> debSet, Collection<Set<Integer>> endSet, int k, String outFile)
	{
		this.fileIn = graph;
		String prefixe = outFile.substring(0, outFile.lastIndexOf("."));
		this.outFile = outFile;
		this.k = k;
		this.stats = prefixe + "_stats.txt";
		this.debElem = debSet;
		this.finElem = endSet;
	}
	
	public static void set_NB_THREADS(int nBTHREADS) {
		NB_THREADS = nBTHREADS;
	}

	public void setLabelsGroupes(LabelGroupeSommets<Set<Integer>> labels)
	{
		this.labels = labels;
	}

	public void setElems(List<Set<Integer>> debElem, List<Set<Integer>> finElem)
	{
		this.debElem = debElem;
		this.finElem = finElem;
	}
	
	private static DefaultDirectedWeightedGraph<Integer, DefaultWeightedEdge> chargeGraphe(String fileIn) throws IOException
	{
		System.err.println(fileIn);
		DefaultDirectedWeightedGraph<Integer, DefaultWeightedEdge> graph;
		graph = new DefaultDirectedWeightedGraph<Integer, DefaultWeightedEdge>(DefaultWeightedEdge.class);
		BufferedReader in;
		in = new BufferedReader (new FileReader (fileIn));
		// ajout sommets
		String ch = in.readLine();
		Integer number = Integer.valueOf(ch);
		for (int i=0; i<number;i++)
		{
			graph.addVertex((Integer)i);
		}

		//ajout arcs
		Matcher m;
		//String tag = "(?:([0-9]+)\\s+([0-9]+)\\s+((?:[0-9]+(?:\\.[0-9]+)?)))";
		String tag = "(?:([0-9]+)\\s+([0-9]+)\\s+(.+))";
		Pattern reco = Pattern.compile(tag);
		int j = 0;
		while ((ch = in.readLine()) != null)
		{
			try
			{
				m = reco.matcher(ch);
				while (m.find())
				{
					Integer src = Integer.valueOf(m.group(1));
					Integer dest = Integer.valueOf(m.group(2));
					Double poids = Double.valueOf(m.group(3));
					graph.addEdge(src, dest);
					graph.setEdgeWeight(graph.getEdge(src, dest), poids);
					j++;
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
		//System.err.println(graph);
		return graph;
	}

	public List<Integer> removeIsolatedVertices(DefaultDirectedWeightedGraph<Integer, DefaultWeightedEdge> graph)
	{
		// filtrage des sommets isoles
		List<Integer> isole = new LinkedList<Integer>();
		for(int i : graph.vertexSet())
		{
			if (graph.inDegreeOf(i) == 0 && graph.outDegreeOf(i) ==0)
			{
				isole.add(i);
			}
		}
		for(int i : isole)
		{
			graph.removeVertex(i);
		}
		return isole;
	}


	/**
	 * @param args
	 */
	public void run() {

		Date deb = new Date(java.lang.System.currentTimeMillis());

		try{
			this.graph = chargeGraphe(fileIn);
			int number = graph.vertexSet().size(); // nombre de sommets
			removeIsolatedVertices(graph).clear();
			
			
			Mask<Integer, DefaultWeightedEdge> mask = new Mask<Integer, DefaultWeightedEdge>();

			//ajout d'une source factice reliee a tous les sommets.
			Integer fakeSrc = -1;
			graph.addVertex(fakeSrc);
			for (int n = 0; n <number; n++)
			{
				if (graph.containsVertex(n))
				{
					graph.addEdge(fakeSrc, n);
					graph.setEdgeWeight(graph.getEdge(fakeSrc, n), 0);
					mask.addEdge(graph.getEdge(fakeSrc, n));
				}
			}

			//ajout d'une destination factice reliee a tous les sommets.
			Integer fakeDest = -2;
			graph.addVertex(fakeDest);
			for (int n = 0; n <number; n++)
			{
				if (graph.containsVertex(n))
				{
					graph.addEdge(n, fakeDest);
					graph.setEdgeWeight(graph.getEdge(n, fakeDest), 0);
					mask.addEdge(graph.getEdge(n, fakeDest));
				}
			}

			BufferedWriter out = new BufferedWriter(new FileWriter(outFile));
			out.close();
			BufferedWriter outStats = new BufferedWriter(new FileWriter(stats));
			outStats.write("Debut : \t" +new Date(java.lang.System.currentTimeMillis())+"\n");
			outStats.close();
			
			Thread t = new ControleCheminsEnParallele(NB_THREADS,graph, fakeSrc, fakeDest, mask, k,
					debElem, finElem, labels, outFile);
			
			t.start();
			t.join();
			
			outStats = new BufferedWriter(new 
					FileWriter(stats, true));
			outStats.write("Fin : \t" +new
					Date(java.lang.System.currentTimeMillis())+"\n");
			outStats.close();

		}
		catch(Exception e){
			e.printStackTrace();
			System.exit(1);
		}
		finally
		{
			Date fin = new Date(java.lang.System.currentTimeMillis());
			System.out.println("Begins at " +deb);
			System.out.println("Ends at " + fin);
			System.out.println("Duration " + (fin.getTime()-deb.getTime()) + "ms");
			//System.err.println("End");
		}
	}


	public static List<DefaultWeightedEdge> cut (List<DefaultWeightedEdge> l)
	{
		l.remove(l.size()-1);
		l.remove(0);
		return l;
	}

	public static List<DefaultWeightedEdge> removeFakeElements(List<DefaultWeightedEdge> path)
	{
		path.remove(0);
		path.remove(path.size()-1);
		return path;
	}



	public static Map<String, Set<Integer>> loadAssoReaction(String fileIn) throws IOException
	{
		return loadAsso(fileIn, "(?:(.+)\\t(.+))");
	}

	public static Map<String, Set<Integer>> loadAssoGene(String fileIn) throws IOException
	{
		return loadAsso(fileIn, "(?:(.+)\\t(.+))");
	}

	public static Map<String, Set<Integer>> loadAsso(String fileIn, String tag) throws IOException
	{
		Map<String, Set<Integer>> result = new HashMap<String, Set<Integer>>();
		BufferedReader in = new BufferedReader (new FileReader (fileIn));
		Matcher m;
		Matcher m2;
		String nb = "(?:([0-9]+))";
		Pattern reco = Pattern.compile(tag);
		Pattern recoNb = Pattern.compile(nb);
		String ch;
		while ((ch = in.readLine()) != null)
		{
			try
			{
				m = reco.matcher(ch);
				while (m.find())
				{
					Set<Integer> set = new HashSet<Integer>();
					result.put(m.group(1), set);
					m2 = recoNb.matcher(m.group(2));
					while (m2.find())
					{
						set.add(Integer.valueOf(m2.group(1)));
					}
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
		in.close();
		return result;
	}


}
